/*
 * multiselect2side jQuery plugin
 *
 * Copyright (c) 2010 Giovanni Casassa (senamion.com - senamion.it)
 *
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://www.senamion.com
 *
 */

(function ($) {
    // SORT INTERNAL
    function internalSort(a, b) {
        var compA = $(a).text().toUpperCase();
        var compB = $(b).text().toUpperCase();
        return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
    };

    var methods = {
        init: function (options) {
            var o = {
                selectedPosition: 'right',
                moveOptions: true,
                labelTop: 'Top',
                labelBottom: 'Bottom',
                labelUp: 'Up',
                labelDown: 'Down',
                labelSort: 'Sort',
                labelsx: 'Available',
                labeldx: 'Selected',
                maxSelected: -1,
                autoSort: false,
                autoSortAvailable: false,
                search: false,
                caseSensitive: false,
                delay: 200,
                optGroupSearch: false,
                minSize: 6
            };

            return this.each(function () {
                var el = $(this);
                var data = el.data('multiselect2side');

                if (options)
                    $.extend(o, options);

                if (!data)
                    el.data('multiselect2side', o);

                var originalName = $(this).attr("name");
                if (originalName.indexOf('[') != -1)
                    originalName = originalName.substring(0, originalName.indexOf('['));

                var nameDx = originalName + "ms2side__dx";
                var nameSx = originalName + "ms2side__sx";
                var size = $(this).attr("size");
                // SIZE MIN
                if (size < o.minSize) {
                    $(this).attr("size", "" + o.minSize);
                    size = o.minSize;
                }

                // UP AND DOWN
                var divUpDown =
                    "<div class='ms2side__updown'>" +
                    "<p class='SelSort' title='Sort'>" + o.labelSort + "</p>" +
                    "<p class='MoveTop' title='Move on top selected option'>" + o.labelTop + "</p>" +
                    "<p class='MoveUp' title='Move up selected option'>" + o.labelUp + "</p>" +
                    "<p class='MoveDown' title='Move down selected option'>" + o.labelDown + "</p>" +
                    "<p class='MoveBottom' title='Move on bottom selected option'>" + o.labelBottom + "</p>" +
                    "</div>";

                // INPUT TEXT FOR SEARCH OPTION
                var leftSearch = false, rightSearch = false;
                // BOTH SEARCH AND OPTGROUP SEARCH
                if (o.search != false && o.optGroupSearch != false) {
                    var ss =
                        o.optGroupSearch + "<select class='small' ><option value=__null__> </option></select> " +
                        o.search + "<input class='small' type='text' /><a href='#'> </a>";

                    if (o.selectedPosition == 'right')
                        leftSearch = ss;
                    else
                        rightSearch = ss;
                }
                else if (o.search != false) {
                    var ss = o.search + "<input type='text' /><a href='#'> </a>";

                    if (o.selectedPosition == 'right')
                        leftSearch = ss;
                    else
                        rightSearch = ss;
                }
                else if (o.optGroupSearch != false) {
                    var ss = o.optGroupSearch + "<select><option value=__null__> </option></select>";

                    if (o.selectedPosition == 'right')
                        leftSearch = ss;
                    else
                        rightSearch = ss;
                }

                // CREATE NEW ELEMENT (AND HIDE IT) AFTER THE HIDDED ORGINAL SELECT
                var htmlToAdd =
                    "<div class='ms2side__div'>" +
                    ((o.selectedPosition != 'right' && o.moveOptions) ? divUpDown : "") +
                    "<div class='ms2side__select'>" +
                    ((o.labelsx || leftSearch != false) ? ("<div class='ms2side__header'>" + (leftSearch != false ? leftSearch : o.labelsx) + "</div>") : "") +
                    "<select title='" + o.labelsx + "' name='" + nameSx + "' id='" + nameSx + "' size='" + size + "' multiple='multiple' ></select>" +
                    "</div>" +
                    "<div class='ms2side__options'>" +
                    ((o.selectedPosition == 'right')
                        ?
                        ("<p class='AddOne' title='Add Selected'>&rsaquo;</p>" +
                            "<p class='AddAll' title='Add All'>&raquo;</p>" +
                            "<p class='RemoveOne' title='Remove Selected'>&lsaquo;</p>" +
                            "<p class='RemoveAll' title='Remove All'>&laquo;</p>")
                        :
                        ("<p class='AddOne' title='选择'><i class='glyphicon glyphicon-chevron-left'></i></p>" +
                            "<p class='AddAll' title='全选'><i class='glyphicon glyphicon-backward'></i></p>" +
                            "<p class='RemoveOne' title='移除'><i class='glyphicon glyphicon-chevron-right'></i></p>" +
                            "<p class='RemoveAll' title='清空'><i class='glyphicon glyphicon-forward'></i></p>")
                    ) +
                    "</div>" +
                    "<div class='ms2side__select'>" +
                    ((o.labeldx || rightSearch != false) ? ("<div class='ms2side__header'>" + (rightSearch != false ? rightSearch : o.labeldx) + "</div>") : "") +
                    "<select title='" + o.labeldx + "' name='" + nameDx + "' id='" + nameDx + "' size='" + size + "' multiple='multiple' ></select>" +
                    "</div>" +
                    ((o.selectedPosition == 'right' && o.moveOptions) ? divUpDown : "") +
                    "</div>";
                el.after(htmlToAdd).hide();

                // ELEMENTS
                var allSel = el.next().children(".ms2side__select").children("select");
                var leftSel = (o.selectedPosition == 'right') ? allSel.eq(0) : allSel.eq(1);
                var rightSel = (o.selectedPosition == 'right') ? allSel.eq(1) : allSel.eq(0);
                // HEIGHT DIV
                var heightDiv = $(".ms2side__select").eq(0).height();

                // SELECT optgroup
                var searchSelect = $();

                // SEARCH INPUT
                var searchInput = $(this).next().find("input:text");
                var removeFilter = searchInput.next().hide();
                var toid = false;
                var searchV = false;

                // SELECT optgroup - ADD ALL OPTGROUP AS OPTION
                if (o.optGroupSearch != false) {
                    var lastOptGroupSearch = false;

                    searchSelect = $(this).next().find("select").eq(0);

                    el.children("optgroup").each(function () {
                        if (searchSelect.find("[value='" + $(this).attr("label") + "']").size() == 0)
                            searchSelect.append("<option value='" + $(this).attr("label") + "'>" + $(this).attr("label") + "</option>");
                    });
                    searchSelect.change(function () {
                        var sEl = $(this);

                        if (sEl.val() != lastOptGroupSearch) {
                            // IF EXIST SET SEARCH TEXT TO VOID
                            if (searchInput.val() != "") {
                                clearTimeout(toid);
                                removeFilter.hide();
                                searchInput.val("");//.trigger('keyup');
                                searchV = "";
                                // fto();
                            }

                            setTimeout(function () {
                                if (sEl.val() == "__null__") {
                                    els = el.find("option:not(:selected)");
                                }
                                else
                                    els = el.find("optgroup[label='" + sEl.val() + "']").children("option:not(:selected)");

                                // REMOVE ORIGINAL ELEMENTS AND ADD OPTION OF OPTGROUP SELECTED
                                leftSel.find("option").remove();
                                els.each(function () {
                                    leftSel.append($(this).clone());
                                });
                                lastOptGroupSearch = sEl.val();
                                leftSel.trigger('change');
                            }, 100);
                        }
                    });
                }

                // SEARCH FUNCTION
                var fto = function () {
                    var els = leftSel.children();
                    var toSearch = el.find("option:not(:selected)");

                    // RESET OptGroupSearch
                    lastOptGroupSearch = "__null__";
                    searchSelect.val("__null__");

                    if (searchV == searchInput.val())
                        return;

                    searchInput
                        .addClass("wait")
                        .removeAttr("style");

                    searchV = searchInput.val();

                    // A LITTLE TIMEOUT TO VIEW WAIT CLASS ON INPUT ON IE
                    setTimeout(function () {
                        leftSel.children().remove();
                        if (searchV == "") {
                            toSearch.clone().appendTo(leftSel).removeAttr("selected");
                            removeFilter.hide();
                        }
                        else {
                            toSearch.each(function () {
                                var myText = $(this).text();

                                if (o.caseSensitive)
                                    find = myText.indexOf(searchV);
                                else
                                    find = myText.toUpperCase().indexOf(searchV.toUpperCase());

                                if (find != -1)
                                    $(this).clone().appendTo(leftSel).removeAttr("selected");
                            });

                            if (leftSel.children().length == 0)
                                searchInput.css({ 'border': '1px red solid' });

                            removeFilter.show();
                            leftSel.trigger('change');
                        }
                        leftSel.trigger('change');
                        searchInput.removeClass("wait");
                    }, 5);
                };

                // REMOVE FILTER ON SEARCH FUNCTION
                removeFilter.click(function () {
                    clearTimeout(toid);
                    searchInput.val("");
                    fto();
                    return false;
                });

                // ON CHANGE TEXT INPUT
                searchInput.keyup(function () {
                    clearTimeout(toid);
                    toid = setTimeout(fto, o.delay);
                });

                // CENTER MOVE OPTIONS AND UPDOWN OPTIONS
                $(this).next().find('.ms2side__options, .ms2side__updown').each(function () {
                    var top = ((heightDiv / 2) - ($(this).height() / 2));
                    if (top > 0)
                        $(this).css('padding-top', top + 'px');
                })

                // MOVE SELECTED OPTION TO RIGHT, NOT SELECTED TO LEFT
                $(this).find("option:selected").clone().appendTo(rightSel); // .removeAttr("selected");
                $(this).find("option:not(:selected)").clone().appendTo(leftSel);

                // SELECT FIRST LEFT ITEM AND DESELECT IN RIGHT (NOT IN IE6)
                if (!($.browser.msie && $.browser.version == '6.0')) {
                    leftSel.find("option").eq(0).attr("selected", true);
                    rightSel.children().removeAttr("selected");
                }

                // ON CHANGE SORT SELECTED OPTIONS
                var nLastAutosort = 0;
                if (o.autoSort)
                    allSel.change(function () {
                        var selectDx = rightSel.find("option");

                        if (selectDx.length != nLastAutosort) {
                            // SORT SELECTED ELEMENT
                            selectDx.sort(internalSort);
                            // FIRST REMOVE FROM ORIGINAL SELECT
                            el.find("option:selected").remove();
                            // AFTER ADD ON ORIGINAL AND RIGHT SELECT
                            selectDx.each(function () {
                                rightSel.append($(this).clone());
                                $(this).appendTo(el).attr("selected", true);
                                //el.append($(this).attr("selected", true));		HACK IE6
                            });
                            nLastAutosort = selectDx.length;
                        }
                    });

                // ON CHANGE SORT AVAILABLE OPTIONS (NOT NECESSARY IN ORIGINAL SELECT)
                var nLastAutosortAvailable = 0;
                if (o.autoSortAvailable)
                    allSel.change(function () {
                        var selectSx = leftSel.find("option");

                        if (selectSx.length != nLastAutosortAvailable) {
                            // SORT SELECTED ELEMENT
                            selectSx.sort(internalSort);
                            // REMOVE ORIGINAL ELEMENTS AND ADD SORTED
                            leftSel.find("option").remove();
                            selectSx.each(function () {
                                leftSel.append($(this).clone());
                            });
                            nLastAutosortAvailable = selectSx.length;
                        }
                    });

                // ON CHANGE REFRESH ALL BUTTON STATUS
                allSel.change(function () {
                    // HACK FOR IE6 (SHOW AND HIDE ORIGINAL SELECT)
                    if ($.browser.msie && $.browser.version == '6.0')
                        el.show().hide();
                    var div = $(this).parent().parent();
                    var selectSx = leftSel.children();
                    var selectDx = rightSel.children();
                    var selectedSx = leftSel.find("option:selected");
                    var selectedDx = rightSel.find("option:selected");

                    if (selectedSx.size() == 0 ||
                        (o.maxSelected >= 0 && (selectedSx.size() + selectDx.size()) > o.maxSelected))
                        div.find(".AddOne").addClass('ms2side__hide');
                    else
                        div.find(".AddOne").removeClass('ms2side__hide');

                    // FIRST HIDE ALL
                    div.find(".RemoveOne, .MoveUp, .MoveDown, .MoveTop, .MoveBottom, .SelSort").addClass('ms2side__hide');
                    if (selectDx.size() > 1)
                        div.find(".SelSort").removeClass('ms2side__hide');
                    if (selectedDx.size() > 0) {
                        div.find(".RemoveOne").removeClass('ms2side__hide');
                        // ALL SELECTED - NO MOVE
                        if (selectedDx.size() < selectDx.size()) {	// FOR NOW (JOE) && selectedDx.size() == 1
                            if (selectedDx.val() != selectDx.val())	// FIRST OPTION, NO UP AND TOP BUTTON
                                div.find(".MoveUp, .MoveTop").removeClass('ms2side__hide');
                            if (selectedDx.last().val() != selectDx.last().val())	// LAST OPTION, NO DOWN AND BOTTOM BUTTON
                                div.find(".MoveDown, .MoveBottom").removeClass('ms2side__hide');
                        }
                    }

                    if (selectSx.size() == 0 ||
                        (o.maxSelected >= 0 && selectSx.size() >= o.maxSelected))
                        div.find(".AddAll").addClass('ms2side__hide');
                    else
                        div.find(".AddAll").removeClass('ms2side__hide');

                    if (selectDx.size() == 0)
                        div.find(".RemoveAll").addClass('ms2side__hide');
                    else
                        div.find(".RemoveAll").removeClass('ms2side__hide');
                });

                // DOUBLE CLICK ON LEFT SELECT OPTION
                leftSel.dblclick(function () {
                    $(this).find("option:selected").each(function (i, selected) {
                        if (o.maxSelected < 0 || rightSel.children().size() < o.maxSelected) {
                            $(this).remove().appendTo(rightSel);
                            el.find("[value='" + $(selected).val() + "']").remove().appendTo(el).attr("selected", true);
                        }
                    });
                    $(this).trigger('change');
                });

                // DOUBLE CLICK ON RIGHT SELECT OPTION
                rightSel.dblclick(function () {
                    $(this).find("option:selected").each(function (i, selected) {
                        $(this).remove().appendTo(leftSel);
                        el.find("[value='" + $(selected).val() + "']").removeAttr("selected").remove().appendTo(el);
                    });
                    $(this).trigger('change');

                    // TRIGGER CHANGE AND VALUE NULL FORM OPTGROUP SEARCH (IF EXIST)
                    searchSelect.val("__null__").trigger("change");
                    // TRIGGER CLICK ON REMOVE FILTER (IF EXIST)
                    removeFilter.click();
                });

                // CLICK ON OPTION
                $(this).next().find('.ms2side__options').children().click(function () {
                    if (!$(this).hasClass("ms2side__hide")) {
                        if ($(this).hasClass("AddOne")) {
                            leftSel.find("option:selected").each(function (i, selected) {
                                $(this).remove().appendTo(rightSel);
                                el.find("[value='" + $(selected).val() + "']").remove().appendTo(el).attr("selected", true);
                            });
                        }
                        else if ($(this).hasClass("AddAll")) {	// ALL SELECTED
                            // TEST IF HAVE A FILTER OR A SELECT OPTGROUP
                            if (removeFilter.is(":visible") || (searchSelect.length > 0 && searchSelect.val() != "__null__"))
                                leftSel.children().each(function (i, selected) {
                                    $(this).remove().appendTo(rightSel);
                                    el.find("[value='" + $(selected).val() + "']").remove().appendTo(el).attr("selected", true);
                                });
                            else {
                                leftSel.children().remove().appendTo(rightSel);
                                el.find('option').attr("selected", true);
                                // el.children().attr("selected", true); -- PROBLEM WITH OPTGROUP
                            }
                        }
                        else if ($(this).hasClass("RemoveOne")) {
                            rightSel.find("option:selected").each(function (i, selected) {
                                $(this).remove().appendTo(leftSel);
                                el.find("[value='" + $(selected).val() + "']").remove().appendTo(el).removeAttr("selected");
                            });
                            // TRIGGER CLICK ON REMOVE FILTER (IF EXIST)
                            removeFilter.click();
                            // TRIGGER CHANGE AND VALUE NULL FORM OPTGROUP SEARCH (IF EXIST)
                            searchSelect.val("__null__").trigger("change");
                        }
                        else if ($(this).hasClass("RemoveAll")) {	// ALL REMOVED
                            rightSel.children().appendTo(leftSel);
                            rightSel.children().remove();
                            el.find('option').removeAttr("selected");
                            //el.children().removeAttr("selected"); -- PROBLEM WITH OPTGROUP
                            // TRIGGER CLICK ON REMOVE FILTER (IF EXIST)
                            removeFilter.click();
                            // TRIGGER CHANGE AND VALUE NULL FORM OPTGROUP SEARCH (IF EXIST)
                            searchSelect.val("__null__").trigger("change");
                        }
                    }

                    leftSel.trigger('change');
                });

                // CLICK ON UP - DOWN
                $(this).next().find('.ms2side__updown').children().click(function () {
                    var selectedDx = rightSel.find("option:selected");
                    var selectDx = rightSel.find("option");

                    if (!$(this).hasClass("ms2side__hide")) {
                        if ($(this).hasClass("SelSort")) {
                            // SORT SELECTED ELEMENT
                            selectDx.sort(internalSort);
                            // FIRST REMOVE FROM ORIGINAL SELECT
                            el.find("option:selected").remove();
                            // AFTER ADD ON ORIGINAL AND RIGHT SELECT
                            selectDx.each(function () {
                                rightSel.append($(this).clone().attr("selected", true));
                                el.append($(this).attr("selected", true));
                            });
                        }
                        else if ($(this).hasClass("MoveUp")) {
                            var prev = selectedDx.first().prev();
                            var hPrev = el.find("[value='" + prev.val() + "']");

                            selectedDx.each(function () {
                                $(this).insertBefore(prev);
                                el.find("[value='" + $(this).val() + "']").insertBefore(hPrev);	// HIDDEN SELECT
                            });
                        }
                        else if ($(this).hasClass("MoveDown")) {
                            var next = selectedDx.last().next();
                            var hNext = el.find("[value='" + next.val() + "']");

                            selectedDx.each(function () {
                                $(this).insertAfter(next);
                                el.find("[value='" + $(this).val() + "']").insertAfter(hNext);	// HIDDEN SELECT
                            });
                        }
                        else if ($(this).hasClass("MoveTop")) {
                            var first = selectDx.first();
                            var hFirst = el.find("[value='" + first.val() + "']");

                            selectedDx.each(function () {
                                $(this).insertBefore(first);
                                el.find("[value='" + $(this).val() + "']").insertBefore(hFirst);	// HIDDEN SELECT
                            });
                        }
                        else if ($(this).hasClass("MoveBottom")) {
                            var last = selectDx.last();
                            var hLast = el.find("[value='" + last.val() + "']");

                            selectedDx.each(function () {
                                last = $(this).insertAfter(last);	// WITH last = SAME POSITION OF SELECTED OPTION AFTER MOVE
                                hLast = el.find("[value='" + $(this).val() + "']").insertAfter(hLast);	// HIDDEN SELECT
                            });
                        }
                    }

                    leftSel.trigger('change');
                });

                // HOVER ON OPTION
                $(this).next().find('.ms2side__options, .ms2side__updown').children().hover(
                    function () {
                        $(this).addClass('ms2side_hover');
                    },
                    function () {
                        $(this).removeClass('ms2side_hover');
                    }
                );

                // UPDATE BUTTON ON START
                leftSel.trigger('change');
                // SHOW WHEN ALL READY
                $(this).next().show();
            });
        },
        destroy: function () {
            return this.each(function () {
                var el = $(this);
                var data = el.data('multiselect2side');

                if (!data)
                    return;

                el.show().next().remove();
            });
        },
        addOption: function (options) {
            var oAddOption = {
                name: false,
                value: false,
                selected: false
            };

            return this.each(function () {
                var el = $(this);
                var data = el.data('multiselect2side');

                if (!data)
                    return;

                if (options)
                    $.extend(oAddOption, options);

                var strEl = "<option value='" + oAddOption.value + "' " + (oAddOption.selected ? "selected" : "") + " >" + oAddOption.name + "</option>";

                el.append(strEl);

                // ELEMENTS
                var allSel = el.next().children(".ms2side__select").children("select");
                var leftSel = (data.selectedPosition == 'right') ? allSel.eq(0) : allSel.eq(1);
                var rightSel = (data.selectedPosition == 'right') ? allSel.eq(1) : allSel.eq(0);

                if (oAddOption.selected)
                    rightSel.append(strEl).trigger('change');
                else
                    leftSel.append(strEl).trigger('change');
            });
        }
    };

    $.fn.multiselect2side = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.multiselect2side');
        }
    };
})(jQuery);